'''
Created on Apr 4, 2013

@author: david
'''
import logging
import urllib

from google.appengine.api import mail

from app.Ranger.model import Dealer, Account, DealerAccount, RangerEvent
from pspy import Notification
from string import Template

#---------------------
#---   Constants   ---
#---------------------
PROC_URL = "%s%s"

PROC_DATA = "VIN=%s"
LIMIT = 100

EV_ERR = -1
EV_NEW = 0
EV_SCAN = 1
EV_BUY = 2

PR_ADDR = "dharmadog888@gmail.com"

RESET_SUBJ = "Payment Ranger-Account Reset Request"
RESET_TEXT = """
Hello %s,

We received a password reset for your PaymentRanger account.  If this was done by you,
just click on the link below and you will be taken to the new password page.

     http://pspygear.appspot.com/Ranger?action=access.Reset&c=RlX1E0-%s_927A0C.G5090
     
If this request was not sent by you, we would highly recommend logging into your 
Payment Ranger account ASAP and change your password.
     
Thank You,
Access Agent
PaymentRanger.Com
"""   

TY_SUBJ = "Payment Ranger - Account Activation"
TY_TEXT = """
Welcome %s,

Thank you for creating a Payment Ranger Account. To start using our services, just click 
on the link below to activate your new account.

    http://pspygear.appspot.com/Ranger?action=access.Activate&c=A3L20F-%s_849F9D.E5876
  
Thanks Again,
Access Agent
PaymentRanger.Com 
"""

BUY_SUBJ = "Payment Range - Buyer Alert"
BUY_TEXT = Template("""
This is a Payment Ranger Alert. A customer has clicked the Buy Now button for a vehicle listed on Payment Ranger,

Here are the details:

        VIN:  ${vin}
    Cust ID:  ${pin}
    Vehicle:  ${descr}
    Payment:  ${pymts}
    
Thank You,

Sales Agent
Payment Ranger
""")

#----------------------------
#---   Exception Classes  ---
#----------------------------
class RangerException(Exception):
    pass
class AccessException(RangerException):
    pass

class PspyAgent(object):

    def sendEmailMessage(self,sndr,recvr,body,subj):
        # format and send email
        try:
            mail.send_mail(sender=sndr,
                           to=recvr,
                           subject=subj,
                           body=body)
            
        except Exception as ex:
            Notification("!! Mail Error").setException(ex)
        
    def getServerResponse(self, url, params):
        """
        Send request to server and return
        response...
        """
        rsp = None
        pstr = ""
        
        # format params -> query string
        data = urllib.urlencode(params)
        
        logging.info("~~ encoded data: %s" % data)
        
        try:
            # open connection to server and pass parameters   
            c = urllib.urlopen(url,data)
    
            # get server response
            rsp = c.read()
            
        except Exception as ex:
            # o.o
            raise RangerException(ex)
        
        # return results
        return rsp


#----------------------------
#---    Agent Classes     ---
#----------------------------
class AccessAgent(PspyAgent):
    """
    Access management
    """
    def validate(self,uid,pwd):
        """
        Retrieve and validate credentials
        """
        acct = self.getAccount(uid)
        
        if not acct or acct.pwd != pwd:
            raise AccessException('Invalid Credentials')
        
    def fetchAccount(self,aid):
        """
        Retrieve account by ID
        """
        gql= "WHERE aid = '%s'" % aid
            
        logging.info("~~ GQL: %s" % gql)
        
        acct = Account.gql(gql).get()
            
        return acct
    
    def getAccount(self, uid, pwd=None):
        """
        Retrieve Account by credentials
        """
        gql= "WHERE status = 1 and uid = '%s'" % uid.lower()
        
        if pwd:
            gql = "%s and pwd='%s'" % (gql,pwd)
            
        logging.info("~~ GQL: %s" % gql)
        
        acct = Account.gql(gql).get()
            
        return acct
    
    def sendReset(self,ema):
        """
        Send email to user to allow 
        email reset
        """
        logging.info("~~ Sending Reset Email")
        
        # server side check
        if not mail.is_email_valid(ema):
            raise RangerException("Invalid Email Address")
        
        # fetch account and validate
        acct = self.getAccount(ema)
        if not acct:
            raise AccessException("Active Account for %s not found" % ema)
        
        body = RESET_TEXT % (acct.name,acct.aid)
        
        logging.info("++ Sending acct reset email to %s: %s" % (ema,body))

        self.sendEmailMessage(PR_ADDR, ema, body, RESET_SUBJ)

    def reset(self,aid,pwd):
        """
        Change password
        """
        # fetch account
        gql= "WHERE status = 1 and aid = '%s'" % (aid)
        logging.info("~~ GQL: %s" % gql)
        acct = Account.gql(gql).get()
            
        # update
        acct.pwd = pwd
        acct.put()
        
    def create(self,uid,pwd,name):
        """
        create an account
        """
        acct = None
        # check for existing acct
        try:
            acct = self.getAccount(uid)
        except:
            acct = None
        
        # error if email is already in use
        if acct:
            raise Exception("An account for that Email already exists.")
            
        # build account
        acct = Account()
        acct.uid = uid.lower()
        acct.pwd = pwd
        acct.name = name
        acct.status = 2
        acct.aid = "AC%s" % Account.all().count()
        acct.put()
        
        body = TY_TEXT % (acct.name, acct.aid)
        
        logging.info("~~ Sending thank you letter from %s to %s:\n%s" % (PR_ADDR,uid,body))
        
        # send thank you / welcome letter.
        self.sendEmailMessage(PR_ADDR, uid, body,TY_SUBJ)
        
        return acct
        
    def activate(self,aid):
        """
        Activate a pending / stalled account
        """
        acct = Account.all().filter("aid =",aid).get()
        acct.status=1
        acct.put()
        
        return True
        
class AppAgent(PspyAgent):
    '''
    Application Agent (BPM)
    '''   
        
    def getDealers(self): 
        '''
        Get listing of Dealers for
        selector.
        '''
        dealers = Dealer.all().filter("status =", 1).fetch(LIMIT)
        
        return dealers
        
    def getDealer(self, did):
        '''
        Retrieve selected Dealer
        '''
        logging.info("~~ DID: %s" % did)
        dlr = Dealer.gql("WHERE did = :1", did).get()
        
        return dlr
    
    def getDealerPid(self,acct,did):
        '''
        Retrieve the users pin for a specific dealer...
        '''
        da = DealerAccount.gql("WHERE did=:1 and aid=:2",did,acct).fetch(LIMIT)
        
        logging.info("did: %s;  aid: %s" % (did,acct))
        logging.info("da: %s" % str(da))
        
        return da[0].pid
        
    def buyIt(self, data ):
        dlr = self.getDealer(data['did'])
        
        # create buy event for audit trail and commit
        sev = RangerEvent.gql("WHERE pid=:1, vin=:2, evtype=1 ORDER BY tmstmp DESC", data['pin'],data['vin']).get()
        sev.addBuyEvent()

        logging.info("Sending Buy it note to %s" % dlr.buyit_addr)
        
        data['dlr'] = dlr.name
        
        body = BUY_TEXT.substitute(data)
        
        logging.info("~~ Buy it note:\n %s" % body)
        
        self.sendEmailMessage(PR_ADDR,dlr.buyit_addr,body,BUY_SUBJ)
    
    def procRequest(self, vin, pin, did):
        '''
        Engage server dialog for processing
        on back end.
        '''
        dlr = None
        rv = None
        
        # create event ()
        ev = RangerEvent(pid=pin, did=did, vin=vin)
        
        try:
            # who we talking to?
            dlr = self.getDealer(did)
            logging.info("~~ Dealer: %s" % dlr)    
            # prep
            url = PROC_URL % (dlr.url,dlr.proc_uri % pin)
            data = {"VIN":vin, "action":"Go!"}
            
            # audit
            logging.info("++ calling %s for %s" % (url, str(data)))
            
            # call remote service
            resp = self.getServerResponse(url,data)
            
            # audit
            logging.info("++ Response: %s" % resp)
            
            # parse results
            p1 = resp.split("<td>")[1].strip()
            rv = p1.split("-->")[1].split("</b>")[0].strip().replace("<b>"," ").split("<br>")
            
            # audit
            msg = "Scan Results: %s / %s / %s" % (rv[0],rv[1],rv[2])
            logging.info("~~ %s" % msg)
            ev.descr = msg

        except Exception as ex:
            # catch errors
            Notification("!! Process Error").setException(ex)
            rv = ('err','err','err')
            
            # audit
            ev.evtype = EV_ERR
            ev.descr = "%" % str(ex)
            
        # commit audit trail entry
        ev.put()
        
        # all done!
        return rv
        

